import { Request, Response, NextFunction, RequestHandler } from 'express';
import { ParamsDictionary } from 'express-serve-static-core';
import { ParsedQs } from 'qs';
import { AuthUser } from '../types/auth';
import { TypedRequest } from './routeHandler';

// === Request Types ===
export interface AuthenticatedRequest<
  P extends ParamsDictionary = ParamsDictionary,
  B = any,
  Q extends ParsedQs = ParsedQs
> extends Request<P, any, B, Q> {
  user: AuthUser;
}

// === Handler Types ===
export type AsyncHandler<
  P = ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery = ParsedQs,
> = (
  req: TypedRequest<P, ResBody, ReqBody, ReqQuery>,
  res: Response<ResBody>,
  next: NextFunction
) => Promise<any>;

export type AuthHandler<
  P extends ParamsDictionary = ParamsDictionary,
  B = any,
  Q extends ParsedQs = ParsedQs
> = (
  req: AuthenticatedRequest<P, B, Q>,
  res: Response,
  next: NextFunction
) => Promise<void>;

// === Wrapper Functions ===
export const asyncHandler = <P = any, ResBody = any, ReqBody = any, ReqQuery = any>(
  fn: (
    req: AuthenticatedRequest<P, ResBody, ReqBody, ReqQuery>,
    res: Response<ResBody>,
    next: NextFunction
  ) => Promise<any>
) => {
  return (
    req: AuthenticatedRequest<P, ResBody, ReqBody, ReqQuery>,
    res: Response<ResBody>,
    next: NextFunction
  ) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
};

export function authHandler(handler: AuthHandler): RequestHandler {
  return (req, res, next) => {
    if (!req.user) {
      next(new Error('Authentication required'));
      return;
    }
    Promise.resolve(handler(req as AuthenticatedRequest, res, next)).catch(next);
  };
}

// === Helper Functions ===
export function withAuth<
  P extends ParamsDictionary = ParamsDictionary,
  B = any,
  Q extends ParsedQs = ParsedQs
>(handler: AuthHandler<P, B, Q>): RequestHandler<P, any, B, Q> {
  return (req, res, next) => {
    if (!req.user) {
      next(new Error('Authentication required'));
      return;
    }
    Promise.resolve(handler(req as AuthenticatedRequest<P, B, Q>, res, next)).catch(next);
  };
}

export function withAsync<
  P extends ParamsDictionary = ParamsDictionary,
  B = any,
  Q extends ParsedQs = ParsedQs
>(handler: AsyncHandler<P, B, Q>): RequestHandler<P, any, B, Q> {
  return (req, res, next) => {
    Promise.resolve(handler(req, res, next)).catch(next);
  };
}
